<!DOCTYPE HTML>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>

var imageBitmap;

// Image sources to be passed to createImageBitmap
var canvas;
var image;
var imageBitmap;
var imageData;
var blob;

var imageWidth = 20;
var imageHeight = 20;

// createImageBitmap from canvas
canvas = document.createElement("canvas");
canvas.width = imageWidth;
canvas.height = imageHeight;
var ctx = canvas.getContext("2d");
drawPattern(ctx);
var t_canvas = async_test("Testing createImageBitmap from canvas.");
var runCanvasTests = t_canvas.step_func(function() {
    return runTests(t_canvas, canvas);
});
runCanvasTests();

// createImageBitmap from image and imagebitmap
image = new Image();
var t_image = async_test("Testing createImageBitmap from image.");
image.onload = t_image.step_func(function() {
    async_test(t => {
        createImageBitmap(image).then(function(bitmap) {
            imageBitmap = bitmap;
            var runImageBitmapTests = t.step_func(function() {
                return runTests(t, imageBitmap);
            });
            runImageBitmapTests();
            t.done();
        }, function() {
            promiseRejectedUnexpectedly();
        });
    }, "Testing createImageBitmap from imagebitmap.");
    return runTests(t_image, image);
});
image.src = canvas.toDataURL();

// createImageBitmap from imagedata
imageData = ctx.getImageData(0, 0, 20, 20);
var t_imageData = async_test("Testing createImageBitmap from ImageData.");
var runImageDataTests = t_imageData.step_func(function() {
    return runTests(t_imageData, imageData);
});
runImageDataTests();

// createImageBitmap from blob
var xhr = new XMLHttpRequest();
xhr.open("GET", 'resources/pattern.png');
xhr.responseType = 'blob';
xhr.send();
var t_blob = async_test("Testing createImageBitmap from blob.");
xhr.onload = t_blob.step_func(function() {
    blob = xhr.response;
    return runTests(t_blob, blob);
});

// createImageBitmap from video
var video = document.createElement("video");
var t_video = async_test('Testing createImageBitmap from video.');
video.addEventListener("canplaythrough",
                       t_video.step_func(function() {
                           return runTests(t_video, video);
                        }), false);
video.preload = "auto";
video.src = "resources/pattern.ogv";


function runTests(t, element) {
    imageBitmaps = {};
    var p1 = createImageBitmap(element).then(function (image) {
        imageBitmaps.noCrop = image });
    var p2 = createImageBitmap(element, 0, 0, 10, 10).then(
        function (image) { imageBitmaps.crop = image });
    var p3 = createImageBitmap(element, 5, 5, 10, 10).then(
        function (image) { imageBitmaps.cropCenter = image });
    var p4 = createImageBitmap(element, 10, 10, 10, 10).then(
        function (image) { imageBitmaps.cropRight = image });
    var p5 = createImageBitmap(element, -10, -10, 60, 60).then(
        function (image) { imageBitmaps.overCrop = image });
    var p6 = createImageBitmap(element, 10, 10, 50, 50).then(
        function (image) { imageBitmaps.overCropRight = image });
    var p7 = createImageBitmap(element, 10, 10, -10, -10).then(
        function (image) { imageBitmaps.negativeCrop = image });
    var p8 = createImageBitmap(element, -30, -30, 30, 30).then(
        function (image) { imageBitmaps.blankImage1 = image });
    var p9 = createImageBitmap(element, 40, 30, 30, 30).then(
        function (image) { imageBitmaps.blankImage2 = image });

    return Promise.all([p1, p2, p3, p4, p5, p6, p7, p8, p9]).then(
        t.step_func_done(function() {

        var acanvas = document.createElement("canvas");
        acanvas.setAttribute("width", "50");
        acanvas.setAttribute("height", "50");
        var actx = acanvas.getContext("2d");

        var tolerance = 0;
        // Strictly the same under software rendering, but significantly
        // different on GPU.
        if (jsWrapperClass(element) == "HTMLVideoElement")
            tolerance = 80;

        checkNoCrop(actx, imageBitmaps.noCrop, tolerance);
        checkCrop(actx, imageBitmaps.crop, tolerance);
        checkCropCenter(actx, imageBitmaps.cropCenter, tolerance);
        checkCropRight(actx, imageBitmaps.cropRight, tolerance);
        checkOverCrop(actx, imageBitmaps.overCrop, tolerance);
        checkOverCropRight(actx, imageBitmaps.overCropRight, tolerance);
        checkCrop(actx, imageBitmaps.negativeCrop, tolerance);
        checkBlankImageBitmap(actx, imageBitmaps.blankImage1, tolerance);
        checkBlankImageBitmap(actx, imageBitmaps.blankImage2, tolerance);

        async_test(t => {
            createImageBitmap(element, 0, 0, Math.pow(10, 6),
                                             Math.pow(10, 6)).then(function() {
                promiseResolvedUnexpectedly(
                    'Asking for a huge ImageBitmap unexpectedly resolved.');
                t.done();
            }, function() {
                t.done();
            });
        }, "Asking for a huge ImageBitmap from " + jsWrapperClass(element) + " should be rejected.");

    }), t.step_func_done(function() {
        promiseRejectedUnexpectedly();
    }));
}

//---------------------------- Helper functions --------------------------------

function jsWrapperClass(node)
{
    // returns the ClassName of node
    if (!node)
        return "[null]";
    var string = Object.prototype.toString.apply(node);

    // string will be of the form [object ClassName]
    return string.substr(8, string.length - 9);
}

function shouldBeType(expression, className)
{
    assert_equals(jsWrapperClass(expression), className);
}

function promiseRejectedUnexpectedly() {
    assert_true(false, "createImageBitmap promise rejected.");
}

function promiseResolvedUnexpectedly(message) {
    assert_true(false, message);
}

var redPixel = [255, 0, 0, 255];
var greenPixel = [0, 255, 0, 255];
var bluePixel = [0, 0, 255, 255];
var blackPixel = [0, 0, 0, 255];
var transparentPixel = [0, 0, 0, 0];

function checkPixelsRef(refPixel, ctx, pixels, tolerance) {
    for (var i = 0; i < pixels.length; i++) {
        d = ctx.getImageData(pixels[i][0], pixels[i][1], 1, 1).data;
        assert_array_approx_equals(d, refPixel, tolerance);
    }
}

function checkPixels(ctx, redPixels, greenPixels, bluePixels, blackPixels,
         transparentPixels, tolerance) {
    checkPixelsRef(redPixel, ctx, redPixels, tolerance);
    checkPixelsRef(greenPixel, ctx, greenPixels, tolerance);
    checkPixelsRef(bluePixel, ctx, bluePixels, tolerance);
    checkPixelsRef(blackPixel, ctx, blackPixels, tolerance);
    checkPixelsRef(transparentPixel, ctx, transparentPixels, tolerance);
}

function clearContext(ctx, width, height) {
    ctx.clearRect(0, 0, width, height);
}

function drawPattern(ctx) {
    // Draw a four-color pattern
    ctx.beginPath();
    ctx.fillStyle = "rgb(255, 0, 0)";
    ctx.fillRect(0, 0, 10, 10);
    ctx.fillStyle = "rgb(0, 255, 0)";
    ctx.fillRect(10, 0, 10, 10);
    ctx.fillStyle = "rgb(0, 0, 255)";
    ctx.fillRect(0, 10, 10, 10);
    ctx.fillStyle = "rgb(0, 0, 0)";
    ctx.fillRect(10, 10, 10, 10);
}

function checkNoCrop(ctx, imageBitmap, tolerance) {
    shouldBeType(imageBitmap, "ImageBitmap");
    assert_equals(imageBitmap.width, 20);
    assert_equals(imageBitmap.height, 20);

    // should be drawn to (0, 0), (20, 20)
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0);
    redPixels = [[9, 9]];
    greenPixels = [[11, 9]];
    bluePixels = [[9, 11]];;
    blackPixels = [[11, 11], [19, 19]];
    transparentPixels = [[1, 21], [21, 1], [21, 21]];
    checkPixels(ctx, redPixels, greenPixels, bluePixels, blackPixels,
                transparentPixels, tolerance);

    // shrunk to (0, 0), (10, 10)
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0, 10, 10);
    redPixels = [[4, 4]];
    greenPixels = [[6, 4]];
    bluePixels = [[4, 6]];
    blackPixels = [[6, 6], [9, 9]];
    transparentPixels = [[1, 11], [11, 1], [11, 11]];
    checkPixels(ctx, redPixels, greenPixels, bluePixels, blackPixels,
                transparentPixels, tolerance);

    // shrunk to (10, 10), (20, 20)
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 10, 10, 10, 10);
    redPixels = [[14, 14]];
    greenPixels = [[16, 14]];
    bluePixels = [[14, 16]];
    blackPixels = [[16, 16], [19, 19]];
    transparentPixels = [[11, 21], [21, 11], [21, 21]];
    checkPixels(ctx, redPixels, greenPixels, bluePixels, blackPixels,
                transparentPixels, tolerance);

    // black should be drawn to (10, 10), (20, 20)
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 10, 10, 10, 10, 10, 10, 10, 10);
    blackPixels = [[11, 11], [19, 19]];
    transparentPixels = [[9, 9], [1, 21], [21, 1], [21, 21]];
    checkPixels(ctx, [], [], [], blackPixels, transparentPixels, tolerance);
}

function checkCrop(ctx, imageBitmap, tolerance) {
    shouldBeType(imageBitmap, "ImageBitmap");
    assert_equals(imageBitmap.width, 10);
    assert_equals(imageBitmap.height, 10);

    // red should be drawn to (0, 0), (10, 10)
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0);
    redPixels = [[0, 1], [1, 0], [1, 1], [9, 9]];
    transparentPixels = [[12, 12], [1, 12], [12, 1]];
    checkPixels(ctx, redPixels, [], [], [], transparentPixels, tolerance);

    // red should be drawn to (0, 0), (20, 20)
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0, 20, 20);
    redPixels = [[1, 1], [18, 18]];
    transparentPixels = [[22, 22], [1, 22], [22, 1]];
    checkPixels(ctx, redPixels, [], [], [], transparentPixels, tolerance);
}

function checkCropCenter(ctx, imageBitmap, tolerance) {
    shouldBeType(imageBitmap, "ImageBitmap");
    assert_equals(imageBitmap.width, 10);
    assert_equals(imageBitmap.height, 10);

    // should be drawn to (0, 0), (10, 10) with all four colors
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0);
    redPixels = [[4, 4]];
    greenPixels = [[6, 4]];
    bluePixels = [[4, 6]];
    blackPixels = [[6, 6], [9, 9]];
    transparentPixels = [[11, 11], [1, 11], [11, 1]];
    checkPixels(ctx, redPixels, greenPixels, bluePixels, blackPixels,
                transparentPixels, tolerance);

    // should be drawn to (0, 0), (20, 20) with all four colors
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0, 20, 20);
    redPixels = [[8, 8]];
    greenPixels = [[11, 8]];
    bluePixels = [[8, 11]];
    blackPixels = [[11, 11], [18, 18]];
    transparentPixels = [[22, 22], [1, 21], [21, 1]];
    checkPixels(ctx, redPixels, greenPixels, bluePixels, blackPixels,
                transparentPixels, tolerance);
}

function checkCropRight(ctx, imageBitmap, tolerance) {
    shouldBeType(imageBitmap, "ImageBitmap");
    assert_equals(imageBitmap.width, 10);
    assert_equals(imageBitmap.height, 10);

    // black should be drawn to (0, 0), (10, 10)
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0);
    blackPixels = [[1, 1], [9, 9]];
    transparentPixels = [[11, 11], [1, 11], [11, 1]];
    checkPixels(ctx, [], [], [], blackPixels, transparentPixels, tolerance);
}

function checkOverCrop(ctx, imageBitmap, tolerance) {
    shouldBeType(imageBitmap, "ImageBitmap");
    assert_equals(imageBitmap.width, 60);
    assert_equals(imageBitmap.height, 60);

    // should be drawn to (10, 10), (30, 30)
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0);
    redPixels = [[19, 19]];
    greenPixels = [[21, 19]];
    bluePixels = [[19, 21]];
    blackPixels = [[21, 21], [29, 29]];
    transparentPixels = [[21, 31], [31, 11], [31, 31]];
    checkPixels(ctx, redPixels, greenPixels, bluePixels, blackPixels,
                transparentPixels, tolerance);

    // shrunk to (0, 0), (10, 10)
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0, 10, 10);
    redPixels = [[2, 2]];
    greenPixels = [[4, 2]];
    bluePixels = [[2, 4]];
    blackPixels = [[3, 3], [4, 4]];
    transparentPixels = [[1, 11], [11, 1], [11, 11]];
    checkPixels(ctx, redPixels, greenPixels, bluePixels, blackPixels,
                transparentPixels, tolerance);
}

function checkOverCropRight(ctx, imageBitmap, tolerance) {
    assert_equals(imageBitmap.width, 50);
    assert_equals(imageBitmap.height, 50);

    // black should be drawn to (0, 0), (10, 10)
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0);
    blackPixels = [[1, 1], [9, 9]];
    transparentPixels = [[11, 11], [1, 11], [11, 1]];
    checkPixels(ctx, [], [], [], blackPixels, transparentPixels, tolerance);

    // black should be drawn to (0, 0), (4, 4)
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0, 20, 20);
    blackPixels = [[1, 1], [3, 3]];
    transparentPixels = [[5, 5], [1, 5], [5, 1]];
    checkPixels(ctx, [], [], [], blackPixels, transparentPixels, tolerance);

    // nothing should be drawn
    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 10, 10, 20, 20, 0, 0, 20, 20);
    transparentPixels = [[1, 1], [3, 3], [5, 5], [1, 5], [5, 1]];
    checkPixels(ctx, [], [], [], [], transparentPixels, tolerance);
}

function checkBlankImageBitmap(ctx, imageBitmap, tolerance) {
    shouldBeType(imageBitmap, "ImageBitmap");

    clearContext(ctx, 50, 50);
    ctx.drawImage(imageBitmap, 0, 0);
    transparentPixels = [[1, 1], [9, 9], [11, 11], [21, 21]];
    checkPixels(ctx, [], [], [], [], transparentPixels, tolerance);
}

</script>
</body>
</html>
